package de.fuberlin.osm2.gui.view;

import java.util.*;

import org.mapsforge.android.maps.*;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import de.fuberlin.osm2.R;
import de.fuberlin.osm2.data.DataBaseConnector;
import de.fuberlin.osm2.data.DataBaseConnectorImpl;
import de.fuberlin.osm2.data.dao.TrackData;
import de.fuberlin.osm2.log.Constants;
import de.fuberlin.osm2.trackrecorder.TrackRecorderService;
import org.mapsforge.android.maps.*;

/**
 * MapFrame extends mapsforge's MapView. It shows OSM - informations using the
 * mapsforge - library. In order to load a map, an internet permission is
 * required.
 *
 * @author user
 */
public class MapFrame extends MapView implements
        ServiceLocationListener {

    private Map<Long, ArrayRouteOverlay> wayToOverlayMap = new TreeMap<Long, ArrayRouteOverlay>();

    private TrackRecorderService trackRecorderService;
    private long trackId;
    private String TAG = Constants.MAP_FRAME;

    /**
     * First constructor.
     *
     * @param context context of the calling instance
     */
    public MapFrame(Context context) {
        super(context);
        initMap();
    }

    /**
     * Second constructor.
     *
     * @param context context of the calling instance
     * @param attrs   attribute sets as given from the calling instance
     */
    public MapFrame(Context context, AttributeSet attrs) {
        super(context, attrs);
        initMap();
    }


    private void initMap() {

        this.setClickable(true);
        this.setBuiltInZoomControls(true);
        this.setScaleBar(true);
        this.setMapViewMode(MapViewMode.MAPNIK_TILE_DOWNLOAD);

        this.getController(); //.setZoom(10);

        TrackData trackData = getTrackData();

        for (Map.Entry<Long, Collection<GeoPoint>> wayEntry : trackData.pointMap.entrySet()) {

            boolean partOfArea = trackData.wayIsAreaMap.get(wayEntry.getKey());
            boolean isWay = wayEntry.getKey() > 0;
            Collection<GeoPoint> geoPoints = wayEntry.getValue();
            RouteOverlay overlay = ViewUtils.routeOverlay(geoPoints, isWay, partOfArea);


            this.getOverlays().add(overlay);

        }

    }

    @Override
    public void onLocationUpdate(double longitude, double latitude) {

        invalidate();

        // Add the new point to coordinate list and draw the new point.
        geoCoordinates.add(new GeoPoint(latitude, longitude));

        // Center the map.
        this.getController().setCenter(geoCoordinates.get(geoCoordinates.size() - 1));

        if (ways.size() % 2 != 0) {
            // drawWay();
            return;
        }
        if (areas.size() % 2 != 0) {
            drawArea();
            return;
        }
        if (tracks.size() % 2 != 0) {
            drawTrack();
            return;
        }

    }

    @Override
    public void onPOI(double longitude, double latitude, String newTag) {

        // First step: Create a new POI - marker.
        OverlayItem newPOI = new OverlayItem(new GeoPoint(latitude, longitude), "POI", newTag);

        // Second step: Add it to POI - list.
        poiOverlay.addOverlay(newPOI);

    }

    @Override
    public void onStartWay() {

        // There is no coordinate so we can't create a way.
        if (geoCoordinates.size() == 0) {
            return;
        }

        // There is an uncompleted way.
        if (ways.size() % 2 != 0) {
            return;
        }

        // GeoPoint array.
        GeoPoint[] geoPoints;

        Log.d(Constants.TAG, "track - size:" + tracks.size());
        Log.d(Constants.TAG, "ways - size start 1:" + ways.size());

        // Update the lists.
        ways.add(Integer.valueOf(geoCoordinates.size() - 1));

        Log.d(Constants.TAG, "ways - size start 2:" + ways.size());

        if (tracks.size() % 2 != 0) {
            tracks.add(Integer.valueOf(geoCoordinates.size() - 1));
            /*
                * if (tracks.get(tracks.size() - 1) != tracks.get(tracks.size() -
                * 2)) { geoPoints = new GeoPoint[tracks.get(tracks.size() -
                * 1).intValue() - tracks.get(tracks.size() - 2).intValue() + 1];
                * for (int j = 0; j < geoPoints.length; j++) { geoPoints[j] =
                * geoCoordinates.get(tracks.get(tracks.size() - 2).intValue() + j);
                * } OverlayRoute newTrack = new OverlayRoute(geoPoints,
                * overlayFill, overlayOutline); routeOverlay.addRoute(newTrack); }
                */
        }

        Log.d(Constants.TAG, "track - size:" + tracks.size());

        if (areas.size() % 2 != 0) {
            areas.add(Integer.valueOf(geoCoordinates.size() - 1));
            if (areas.get(areas.size() - 1) != areas.get(areas.size() - 2)) {
                geoPoints = new GeoPoint[areas.get(areas.size() - 1).intValue()
                        - areas.get(areas.size() - 2).intValue() + 2];
                for (int j = 0; j < geoPoints.length - 1; j++) {
                    geoPoints[j] = geoCoordinates.get(areas.get(areas.size() - 2).intValue()
                            + j);
                }
                geoPoints[geoPoints.length - 1] = geoPoints[0];
                OverlayRoute newArea = new OverlayRoute(geoPoints, areaPaint1, areaPaint2);
                routeOverlay.addRoute(newArea);
            }
        }

    }

    @Override
    public void onStopWay() {

        // There is no uncompleted way.
        if (ways.size() % 2 == 0) {
            return;
        }

        // GeoPoint array.
        GeoPoint[] geoPoints;

        Log.d(Constants.TAG, "track - size:" + tracks.size());
        Log.d(Constants.TAG, "ways - size stop 1:" + ways.size());

        // First add the index number.
        ways.add(Integer.valueOf(geoCoordinates.size() - 1));
        tracks.add(Integer.valueOf(geoCoordinates.size() - 1));

        Log.d(Constants.TAG, "ways - size stop 2:" + ways.size());
        Log.d(Constants.TAG, "track - size:" + tracks.size());

        // The way is complete. Lets save it.
        if (ways.get(ways.size() - 1) != ways.get(ways.size() - 2)) {
            geoPoints = new GeoPoint[ways.get(ways.size() - 1).intValue()
                    - ways.get(ways.size() - 2).intValue() + 1];
            for (int j = 0; j < geoPoints.length; j++) {
                geoPoints[j] = geoCoordinates.get(ways.get(ways.size() - 2).intValue() + j);
            }
            OverlayRoute newWay = new OverlayRoute(geoPoints, wayPaint1, wayPaint2);
            routeOverlay.addRoute(newWay);
        }
    }

    @Override
    public void onStartArea() {

        // There is no coordinate so we can't create an area.
        if (geoCoordinates.size() == 0) {
            return;
        }

        // There is an uncompleted area.
        if (areas.size() % 2 != 0) {
            return;
        }

        // GeoPoint array.
        GeoPoint[] geoPoints;

        // Update the lists.
        areas.add(Integer.valueOf(geoCoordinates.size() - 1));

        if (tracks.size() % 2 != 0) {
            tracks.add(Integer.valueOf(geoCoordinates.size() - 1));
            if (tracks.get(tracks.size() - 1) != tracks.get(tracks.size() - 2)) {
                geoPoints = new GeoPoint[tracks.get(tracks.size() - 1).intValue()
                        - tracks.get(tracks.size() - 2).intValue() + 1];
                for (int j = 0; j < geoPoints.length; j++) {
                    geoPoints[j] = geoCoordinates.get(tracks.get(tracks.size() - 2).intValue()
                            + j);
                }
                OverlayRoute newTrack = new OverlayRoute(geoPoints, overlayFill,
                        overlayOutline);
                routeOverlay.addRoute(newTrack);
            }
        }

        if (ways.size() % 2 != 0) {
            ways.add(Integer.valueOf(geoCoordinates.size() - 1));
            if (ways.get(ways.size() - 1) != ways.get(ways.size() - 2)) {
                geoPoints = new GeoPoint[ways.get(ways.size() - 1).intValue()
                        - ways.get(ways.size() - 2).intValue() + 1];
                for (int j = 0; j < geoPoints.length; j++) {
                    geoPoints[j] = geoCoordinates.get(ways.get(ways.size() - 2).intValue() + j);
                }
                OverlayRoute newWay = new OverlayRoute(geoPoints, wayPaint1, wayPaint2);
                routeOverlay.addRoute(newWay);
            }
        }

    }

    @Override
    public void onStopArea() {

        // There is no uncompleted area.
        if (areas.size() % 2 == 0) {
            return;
        }

        // GeoPoint array.
        GeoPoint[] geoPoints;

        // First add the index number.
        areas.add(Integer.valueOf(geoCoordinates.size() - 1));
        tracks.add(Integer.valueOf(geoCoordinates.size() - 1));

        // The area is complete. Lets save it.
        if (areas.size() < 2) {
            return;
        }
        if (areas.get(areas.size() - 1) != areas.get(areas.size() - 2)) {
            geoPoints = new GeoPoint[areas.get(areas.size() - 1).intValue()
                    - areas.get(areas.size() - 2).intValue() + 2];
            for (int j = 0; j < geoPoints.length - 1; j++) {
                geoPoints[j] = geoCoordinates.get(areas.get(areas.size() - 2).intValue() + j);
            }
            geoPoints[geoPoints.length - 1] = geoPoints[0];
            OverlayRoute newArea = new OverlayRoute(geoPoints, areaPaint1, areaPaint2);
            routeOverlay.addRoute(newArea);
        }
    }

    /**
     * If there is an uncompleted way, drawWay() draws it on the screen.
     */
    private void drawWay() {

        // Refresh the newest way.
        GeoPoint[] tmpWay = new GeoPoint[geoCoordinates.size()
                - ways.get(ways.size() - 1).intValue()];
        for (int i = 0; i < tmpWay.length; i++) {
            tmpWay[i] = geoCoordinates.get(ways.get(ways.size() - 1).intValue() + i);
        }

        routeOverlay.removeOverlay(tempWayRoute);
        tempWayRoute = new OverlayRoute(tmpWay, wayPaint1, wayPaint2);

        routeOverlay.addRoute(tempWayRoute);

    }

    /**
     * If there is an uncompleted track, drawTrack() draws it on the screen.
     */
    private void drawTrack() {

        // Refresh the newest track.
        GeoPoint[] tmpTrack = new GeoPoint[geoCoordinates.size()
                - tracks.get(tracks.size() - 1).intValue()];
        for (int i = 0; i < tmpTrack.length; i++) {
            tmpTrack[i] = geoCoordinates.get(tracks.get(tracks.size() - 1).intValue() + i);
        }

        routeOverlay.removeOverlay(tempTrackRoute);
        tempTrackRoute = new OverlayRoute(tmpTrack, overlayFill, overlayOutline);

        routeOverlay.addRoute(tempTrackRoute);

    }

    /**
     * If there is an uncompleted area, drawArea() draws it on the screen.
     */
    private void drawArea() {

        // Refresh the newest area.
        GeoPoint[] tmpArea = new GeoPoint[geoCoordinates.size()
                - areas.get(areas.size() - 1).intValue() + 1];
        for (int i = 0; i < tmpArea.length - 1; i++) {
            tmpArea[i] = geoCoordinates.get(areas.get(areas.size() - 1).intValue() + i);
        }
        tmpArea[tmpArea.length - 1] = tmpArea[0];

        routeOverlay.removeOverlay(tempAreaRoute);
        tempAreaRoute = new OverlayRoute(tmpArea, areaPaint1, areaPaint2);

        routeOverlay.addRoute(tempAreaRoute);

    }

    /**
     * registers the track recorder service with the MapFrame
     *
     * @param recorderService the currently working instance of
     *                        TrackRecorderService
     */
    public void registerService(TrackRecorderService recorderService) {
        this.trackRecorderService = recorderService;
    }


    public void setTrackId(long trackId) {
        this.trackId = trackId;
    }

    private TrackData getTrackData() {
        DataBaseConnector dbc = new DataBaseConnectorImpl(this.getContext());
        Log.d(TAG, String.format("initializing map for track id %d", trackId));
        TrackData trackData = dbc.getTrackData(trackId);
        dbc.close();
        return trackData;
    }

}
